/*
 *  cda/src/decompress.c
 *
 *  (C) 2011 Harry Wei
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>

#include "../include/cda.h"

/*
 * dcompress_file: decompress the compressed file.
 * @cf: the compressed file's path & name.
 */

int 
dcompress_file(char *cf)
{
	int i = 0;
	int fd;
	int fd_file;
	int counter;
	int ret;
	int case_value;
	int flag = 0;
	char label[4];
	char ch;
	char file_name[FILE_NAME];
	char *check_file = "jiawei";
	uint32_t cur_locate = 0x00000000ul;
	struct huffman *get_huff = NULL;
	struct huffcode *hc = NULL;
	struct ch_fre *effect_fre;
	struct byte rd_cf;

	if (0 > (fd = open(cf, O_RDONLY))) {
		fprintf(stderr, "Open File Error!\n");
		return 1;
	}									/* read label */
	if (sizeof(label) != pread(fd, label, sizeof(label), cur_locate)) {
		fprintf(stderr, "Pread Error!\n");
		return 1;
	} else if (strncmp(label, "cda", 4)) {
		fprintf(stderr, "Sorry, this is not our compressed file :-/\n");
		return 1;
	}									/* read over */
	cur_locate += sizeof(label);
	while ((sizeof(char) == pread(fd, &ch, 
				sizeof(char), cur_locate)) && 			/* read file name */
						('\0' != ch)) {
		cur_locate += sizeof(char);
		file_name[i] = ch;
		i = i + 1;
	}
	cur_locate += sizeof(char);
	file_name[i] = '\0';
	if ((fd_file = creat(check_file, 0644)) < 0) {
		fprintf(stderr, "Creat Error!\n");
		return 1;
	}
	if (sizeof(int) != pread(fd, &counter, sizeof(int), cur_locate)) {	/* read counter */
		fprintf(stderr, "Read Error!\n");
		return 1;
	}
	case_value = counter * 2 - 1;
	cur_locate += sizeof(int);						/* read over */
	get_huff = (struct huffman *)malloc(sizeof(struct huffman) * (counter + 1));
	if (!get_huff) {
		fprintf(stderr, "Allocate Error!\n");
		return 1;
	}
	effect_fre = (struct ch_fre *)malloc(sizeof(struct ch_fre) * (counter + 1));
	if (!effect_fre) {
		fprintf(stderr, "Allocate Error!\n");
		return 1;
	}
	for (i = 1; i < counter + 1; i++) {					/* read effect fre */
		if (sizeof(effect_fre[i]) != pread(fd, &effect_fre[i], 
					sizeof(effect_fre[i]), cur_locate)) {
			fprintf(stderr, "Read Error!\n");
			return 1;
		}
		cur_locate += sizeof(effect_fre[i]);
	}									/* read over */
	if (creat_huffman(get_huff, effect_fre, counter)) {
		return 1;
	}

#if DEBUG == 1
	while (ret = pread(fd, &rd_cf, sizeof(struct byte), cur_locate)) {
		if (ret != sizeof(struct byte)) {
			fprintf(stderr, "Read Error!\n");
			return 1;
		}
		printf("encoding: %d %d %d %d %d %d %d %d\n", 
				rd_cf.bit1, rd_cf.bit2, rd_cf.bit3, rd_cf.bit4,
					rd_cf.bit5,rd_cf.bit6,rd_cf.bit7,rd_cf.bit8);
		cur_locate = cur_locate + sizeof(struct byte);
	}
#endif

#if defined(DEBUG) && (DEBUG == 0)
	while (ret = pread(fd, &rd_cf, sizeof(struct byte), cur_locate)) {
		if (ret != sizeof(struct byte)) {
			fprintf(stderr, "Read Error!\n");
			return 1;
		}
		while (1) {
			if (rd_cf.bit1) {
				flag += 1;
				if ((0 == get_huff[case_value].lchild) &&
					(0 == get_huff[case_value].rchild)) {
					if (sizeof(char) != write(fd_file, 
						&get_huff[case_value].ch, sizeof(char))) {
						fprintf(stderr, "Write Error!\n");
						return 1;
					}
					case_value = counter * 2 - 1;
				}
				case_value = get_huff[case_value].lchild;
				if (8 == flag) {
					cur_locate = cur_locate + sizeof(struct byte);
					flag = 0;
					break;
				}
			} else {
				flag += 1;
				if ((0 == get_huff[case_value].lchild) &&
					(0 == get_huff[case_value].rchild)) {
					if (sizeof(char) != write(fd_file, 
						&get_huff[case_value].ch, sizeof(char))) {
						fprintf(stderr, "Write Error!\n");
						return 1;
					}
					case_value = counter * 2 - 1;
				}
				case_value = get_huff[case_value].rchild;
				if (8 == flag) {
					flag = 0;
					cur_locate = cur_locate + sizeof(struct byte);
					break;
				}
			}
			if (rd_cf.bit2) {
				flag += 1;
				if (0 == get_huff[case_value].lchild &&
					0 == get_huff[case_value].rchild) {
					if (sizeof(char) != write(fd_file, 
						&get_huff[case_value].ch, sizeof(char))) {
						fprintf(stderr, "Write Error!\n");
						return 1;
					}
					case_value = counter * 2 - 1;
				}
				case_value = get_huff[case_value].lchild;
				if (8 == flag) {
					flag = 0;
					cur_locate = cur_locate + sizeof(struct byte);
					break;
				}
			} else {
				flag += 1;
				if (0 == get_huff[case_value].lchild &&
					0 == get_huff[case_value].rchild) {
					if (sizeof(char) != write(fd_file, 
						&get_huff[case_value].ch, sizeof(char))) {
						fprintf(stderr, "Write Error!\n");
						return 1;
					}
					case_value = counter * 2 - 1;
				}
				case_value = get_huff[case_value].rchild;
				if (8 == flag) {
					cur_locate = cur_locate + sizeof(struct byte);
					flag = 0;
					break;
				}
			}
			if (rd_cf.bit3) {
				flag += 1;
				if (0 == get_huff[case_value].lchild &&
					0 == get_huff[case_value].rchild) {
					if (sizeof(char) != write(fd_file, 
						&get_huff[case_value].ch, sizeof(char))) {
						fprintf(stderr, "Write Error!\n");
						return 1;
					}
					case_value = counter * 2 - 1;
				}
				case_value = get_huff[case_value].lchild;
				if (8 == flag) {
					flag = 0;
					cur_locate = cur_locate + sizeof(struct byte);
					break;
				}
			} else {
				flag += 1;
				if (0 == get_huff[case_value].lchild &&
					0 == get_huff[case_value].rchild) {
					if (sizeof(char) != write(fd_file, 
						&get_huff[case_value].ch, sizeof(char))) {
						fprintf(stderr, "Write Error!\n");
						return 1;
					}
					case_value = counter * 2 - 1;
				}
				case_value = get_huff[case_value].rchild;
				if (8 == flag) {
					flag = 0;
					cur_locate = cur_locate + sizeof(struct byte);
					break;
				}
			}
			if (rd_cf.bit4) {
				flag += 1;
				if (0 == get_huff[case_value].lchild &&
					0 == get_huff[case_value].rchild) {
					if (sizeof(char) != write(fd_file, 
						&get_huff[case_value].ch, sizeof(char))) {
						fprintf(stderr, "Write Error!\n");
						return 1;
					}
					case_value = counter * 2 - 1;
				}
				case_value = get_huff[case_value].lchild;
				if (8 == flag) {
					flag = 0;
					cur_locate = cur_locate + sizeof(struct byte);
					break;
				}
			} else {
				flag += 1;
				if (0 == get_huff[case_value].lchild &&
					0 == get_huff[case_value].rchild) {
					if (sizeof(char) != write(fd_file, 
						&get_huff[case_value].ch, sizeof(char))) {
						fprintf(stderr, "Write Error!\n");
						return 1;
					}
					case_value = counter * 2 - 1;
				}
				case_value = get_huff[case_value].rchild;
				if (8 == flag) {
					flag = 0;
					cur_locate = cur_locate + sizeof(struct byte);
					break;
				}
			}
			if (rd_cf.bit5) {
				flag += 1;
				if (0 == get_huff[case_value].lchild &&
					0 == get_huff[case_value].rchild) {
					if (sizeof(char) != write(fd_file, 
						&get_huff[case_value].ch, sizeof(char))) {
						fprintf(stderr, "Write Error!\n");
						return 1;
					}
					case_value = counter * 2 - 1;
				}
				case_value = get_huff[case_value].lchild;
				if (8 == flag) {
					flag = 0;
					cur_locate = cur_locate + sizeof(struct byte);
					break;
				}
			} else {
				flag += 1;
				if (0 == get_huff[case_value].lchild &&
					0 == get_huff[case_value].rchild) {
					if (sizeof(char) != write(fd_file, 
						&get_huff[case_value].ch, sizeof(char))) {
						fprintf(stderr, "Write Error!\n");
						return 1;
					}
					printf("Counter %d, Case_value %d\n", counter, case_value);
					case_value = counter * 2 - 1;
				}
				case_value = get_huff[case_value].rchild;
				if (8 == flag) {
					flag = 0;
					cur_locate = cur_locate + sizeof(struct byte);
					break;
				}
			}
			if (rd_cf.bit6) {
				flag += 1;
				if (0 == get_huff[case_value].lchild &&
					0 == get_huff[case_value].rchild) {
					if (sizeof(char) != write(fd_file, 
						&get_huff[case_value].ch, sizeof(char))) {
						fprintf(stderr, "Write Error!\n");
						return 1;
					}
					case_value = counter * 2 - 1;
				}
				case_value = get_huff[case_value].lchild;
				if (8 == flag) {
					flag = 0;
					cur_locate = cur_locate + sizeof(struct byte);
					break;
				}
			} else {
				flag += 1;
				if (0 == get_huff[case_value].lchild &&
					0 == get_huff[case_value].rchild) {
					if (sizeof(char) != write(fd_file, 
						&get_huff[case_value].ch, sizeof(char))) {
						fprintf(stderr, "Write Error!\n");
						return 1;
					}
					case_value = counter * 2 - 1;
				}
				case_value = get_huff[case_value].rchild;
				if (8 == flag) {
					flag = 0;
					cur_locate = cur_locate + sizeof(struct byte);
					break;
				}
			}
			if (rd_cf.bit7) {
				flag += 1;
				if (0 == get_huff[case_value].lchild &&
					0 == get_huff[case_value].rchild) {
					if (sizeof(char) != write(fd_file, 
						&get_huff[case_value].ch, sizeof(char))) {
						fprintf(stderr, "Write Error!\n");
						return 1;
					}
					case_value = counter * 2 - 1;
				}
				case_value = get_huff[case_value].lchild;
				if (8 == flag) {
					flag = 0;
					cur_locate = cur_locate + sizeof(struct byte);
					break;
				}
			} else {
				flag += 1;
				if (0 == get_huff[case_value].lchild &&
					0 == get_huff[case_value].rchild) {
					if (sizeof(char) != write(fd_file, 
						&get_huff[case_value].ch, sizeof(char))) {
						fprintf(stderr, "Write Error!\n");
						return 1;
					}
					case_value = counter * 2 - 1;
				}
				case_value = get_huff[case_value].rchild;
				if (8 == flag) {
					flag = 0;
					cur_locate = cur_locate + sizeof(struct byte);
					break;
				}
			}
			if (rd_cf.bit8) {
				flag += 1;
				if (0 == get_huff[case_value].lchild &&
					0 == get_huff[case_value].rchild) {
					if (sizeof(char) != write(fd_file, 
						&get_huff[case_value].ch, sizeof(char))) {
						fprintf(stderr, "Write Error!\n");
						return 1;
					}
					case_value = counter * 2 - 1;
				}
				case_value = get_huff[case_value].lchild;
				if (8 == flag) {
					flag = 0;
					cur_locate = cur_locate + sizeof(struct byte);
					break;
				}
			} else {
				flag += 1;
				if (0 == get_huff[case_value].lchild &&
					0 == get_huff[case_value].rchild) {
					if (sizeof(char) != write(fd_file, 
						&get_huff[case_value].ch, sizeof(char))) {
						fprintf(stderr, "Write Error!\n");
						return 1;
					}
					case_value = counter * 2 - 1;
				}
				case_value = get_huff[case_value].rchild;
				if (8 == flag) {
					flag = 0;
					cur_locate = cur_locate + sizeof(struct byte);
					break;
				}
			}
		}
	}
#endif

	if (close(fd_file)) {
		fprintf(stderr, "Close Error!\n");
		return 1;
	}
	if (close(fd)) {
		fprintf(stderr, "Close Error!\n");
		return 1;
	}
	return 0;
}	
